
/**
 * 一个类似于jquery的框架tangth
 * 
 * 
 * 
 * ->  不懂的知识点备注了不懂
 * */ 
;( function( global, factory ) {

	"use strict";

	if ( typeof module === "object" && typeof module.exports === "object" ) {

		// For CommonJS and CommonJS-like environments where a proper `window`
		// is present, execute the factory and get jQuery.
		// For environments that do not have a `window` with a `document`
		// (such as Node.js), expose a factory as module.exports.
		// This accentuates the need for the creation of a real `window`.
		// e.g. var jQuery = require("jquery")(window);
        // See ticket #14549 for more info.
        /**
         * 对于具有适当“窗口”的CommonJS和类CommonJS环境，执行工厂并获取jQuery。
         * 对于没有带“文档”的“窗口”(如Node.js)的环境，将工厂公开为module.exports。
         * 这突出了创建一个真正“窗口”的必要性。
         * 例如var jQuery = require(" jQuery ")(window);
         * 更多信息请参见门票#14549。
         * */ 
		module.exports = global.document ?
			factory( global, true ) :
			function( w ) {  // 形参w
				if ( !w.document ) {
					throw new Error( "T requires a window with a document" );
				}
				return factory( w );
			};
	} else {
		factory( global );
	}

// Pass this if window is not defined yet
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
    "use strict";

    var version = "1.0.0";
    var document = window.document;
    var Tangth = function(selector, context){
        // 相当于 实例化 init -> constructor 指向Tangth, 则相当于实例化自己
        return new Tangth.fn.init( selector, context );
    }

    Tangth.fn = Tangth.prototype = {
        tangth: version,
        constructor: Tangth,
        length: 0,

        each: function( callback ) {
            return Tangth.each( this, callback );
        },
        // Support: Android <=4.0 only, PhantomJS 1 only
        // push.apply(_, arraylike) throws on ancient WebKit
        merge: function( first, second ) {
            var len = +second.length,
                j = 0,
                i = first.length;

            for ( ; j < len; j++ ) {
                first[ i++ ] = second[ j ];
            }

            first.length = i;

            return first;
        },
        pushStack: function( elems ) {
            // Build a new Tangth matched element set
            // this.constructor() // 实例化一个对象
            var ret = Tangth.fn.merge( this.constructor(), elems );
    
            // Add the old object onto the stack (as a reference)
            ret.prevObject = this;
    
            // Return the newly-formed element set
            // 返回一个 新的
            return ret;
        },
    }

    // 正则表达式子
    /**
     * /i (忽略大小写)
        /g (全文查找出现的所有匹配字符)
        /m (多行查找)
        /gi(全文查找、忽略大小写)
        /ig(全文查找、忽略大小写)
     * */ 
    var whitespace = "[\\x20\\t\\r\\n\\f]";

    var reg = {
        // Easily-parseable/retrievable ID or TAG or CLASS selectors
        // 匹配 id 标签 类名
        quickExpr: /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
        trim: new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),

        elem: /^<[a-z]+>/i, // <span>
        elemLast: /<\/[a-z]+>$/i, // </span>
        filterTag: /<|>/g,
    }

    /**
     * 初始化 $(function(){}),  $("#abc"),   $("<div>")
     * */ 

    var init = Tangth.fn.init = function( selector ){
        var match, elem;

		// HANDLE: $(""), $(null), $(undefined), $(false)
		if ( !selector ) {
			return this;
		}

		// Handle HTML strings
		if ( typeof selector === "string" ) {
			if ( selector[ 0 ] === "<" &&
				selector[ selector.length - 1 ] === ">" &&
				selector.length >= 3 ) {

                // Assume that strings that start and end with <> are HTML and skip the regex check
                // 这里需要床架标签
                match = selector.replace( reg.filterTag, ""); //[ null, selector, null ];
                // 获取标签名 过滤掉 <  >
                
                elem = document.createElement(match);
                if ( elem ) {
                    this[ 0 ] = elem;
                    this.length = 1;
                }
                return this;
			} else {
                // 如果 exec() 找到了匹配的文本，则返回一个结果数组。否则，返回 null。
                // match = reg.quickExpr.exec( selector );
                // console.log("match", selector, match);

                elem = document.querySelectorAll(selector);
                for(var i=0; i<elem.length; i++){
                    this[i] = elem[i];
                }
                this.length = i;

                return this;
			}

		// HANDLE: $(DOMElement)  // 不懂
		} else if ( selector.nodeType ) {  // 选中的节点
			this[ 0 ] = selector;
			this.length = 1;
			return this;

		// HANDLE: $(function)
		// Shortcut for document ready
		} else if ( Tangth.isFunction( selector ) ) {
                // Execute immediately if ready is not present
                // 如果没有准备好，请立即执行
				selector( Tangth );
		}

		// return jQuery.makeArray( selector, this );
    }
    // init.prototype.constructor 指向 Tangth
    // 任何一个prototype对象都有一个constructor属性，指向它的构造函数。
    init.prototype = Tangth.fn;  

    Tangth.extend = Tangth.fn.extend = function() {
        // console.log("arguments", arguments);
        // console.log("this", this);
        // return;
        var options, name, src, copy, copyIsArray, clone,
            target = arguments[ 0 ] || {},
            i = 1,
            length = arguments.length,
            deep = false;
    
        // Handle a deep copy situation
        // 处理深度复制的情况
        // $.extend(true, {}, defaluts, options);
        if ( typeof target === "boolean" ) {
            deep = target;
    
            // Skip the boolean and the target
            // 跳过布尔值和目标
            target = arguments[ i ] || {};
            i++;
        }
    
        // Handle case when target is a string or something (possible in deep copy)
        // 当目标是字符串或其他东西时的句柄(可能在深度拷贝中)
        if ( typeof target !== "object" && !Tangth.isFunction( target ) ) {
            target = {};
        }
    
        // Extend jQuery itself if only one argument is passed
        // 如果只传递一个参数，则扩展jQuery本身
        if ( i === length ) {
            target = this;
            i--;
        }
    
        for ( ; i < length; i++ ) {
    
            // Only deal with non-null/undefined values
            // 只处理非空值/未定义值
            if ( ( options = arguments[ i ] ) != null ) {
    
                // Extend the base object
                // 扩展基本对象
                for ( name in options ) {
                    src = target[ name ];  // 被覆盖的目标数据
                    copy = options[ name ];

                    // Prevent never-ending loop
                    // 防止无限循环  // 不懂
                    if ( target === copy ) {
                        continue;
                    }
    
                    // Recurse if we're merging plain objects or arrays
                    // 如果合并普通对象或数组，则递归
                    if ( deep && copy && ( Tangth.isPlainObject( copy ) || ( copyIsArray = Tangth.isArray( copy ) ) ) ) {
                        if ( copyIsArray ) { // 数组
                            copyIsArray = false;
                            clone = src && Tangth.isArray( src ) ? src : [];
    
                        } else {  // 对象
                            clone = src && Tangth.isPlainObject( src ) ? src : {};
                        }

                        // 递归
                        // Never move original objects, clone them
                        // 不要移动原始对象，克隆它们
                        target[ name ] = Tangth.extend( deep, clone, copy );
    
                    // Don't bring in undefined values
                    // 不要引入未定义的值   这里是 字符串 、函数
                    } else if ( copy !== undefined ) {
                        // console.log("copy undefind：", name);
                        target[ name ] = copy;
                    }
                }
            }
        }
    
        // Return the modified object
        // 返回修改后的对象
        return target;
    };

    function returnTrue() {
        return true;
    }
    
    function returnFalse() {
        return false;
    }

    /**
     * ele.on("click.one", fn)
     * ele.off("click.one")
     * ele.one("")
     * ele.bind()
     * */ 
    function on( elem, type, fn, one ){
        var origFn;

        if ( fn === false ) {
            fn = returnFalse;
        } else if ( !fn ) {
            return elem;
        }

        // 只执行一次
        if( one === 1 ){

            origFn = fn;
            fn = function( event ) {

                // Can use an empty set, since event contains the info
                // 可以使用空集，因为事件包含信息
                Tangth().off( event );
                return origFn.apply( this, arguments );
            };

        }

        return elem.each( function() {
            this.nodeType == 1 ? Tangth.event.add( this, type, fn): null;
        } );
    }

    function off( event, types, namespace ){
        var obj, handler,
            x = 0, elem = this;

        if( !types || !Tangth.isArray(types) ) return this;

        for( ; x < types.length; x++){
            obj = types[x];
            // 如果namespace is not undefined, so 
            if( namespace && namespace !== obj.namespace) continue;

            handler = obj.handler;

            if (elem.removeEventListener) {                    //所有主流浏览器，除了 IE 8 及更早 IE版本
                elem.removeEventListener(event, handler);
            } else if (elem.detachEvent) {                  // IE 8 及更早 IE 版本  注意：此方法只有冒泡阶段。
                elem.detachEvent(event, handler);
            }

            // delete types[x]
        }


        return this;

    }

    /*
    * Helper functions for managing events -- not part of the public interface.
    * 用于管理事件的Helper函数——不是公共接口的一部分。
    * Props to Dean Edwards' addEvent library for many of the ideas.
    * 迪安·爱德华兹的addEvent图书馆为他的许多想法提供了支持。
    */

   Tangth.event = {
       add: function( elem, type, fn ){
            var events, handler,
                namespace,
                types,
                obj,
                elemData = dataPriv.get(elem);
            
            if ( !elemData || Tangth.isEmptyString( type ) ) {
                return;
            }

            // 以 dot / space 分割
            types = type.split(/\.|\s/);
            // 事件 eg. click
            type = types[0];  
            // 函数名 
            namespace = types[1] || "";

            if( Tangth.isEmptyString( type ) ) return;

            // Init the element's event structure and main handler, if this is the first
            // 初始化元素的事件结构和主处理程序(如果这是第一个)
            if ( !( events = elemData.events ) ) {
                events = elemData.events = {};
            }

            if( !Tangth.isArray(events[type]) ){
                events[type] = [];
            }

            obj = {
                "type": type,
                "namespace": namespace,
                "handler": fn
            }

            events[type].push(obj);

            if(elem.nodeType == 1){
                handler = obj.handler;
                if (elem.addEventListener) {                    //所有主流浏览器，除了 IE 8 及更早 IE版本
                    elem.addEventListener(type, handler);
                } else if (elem.attachEvent) {                  // IE 8 及更早 IE 版本  注意：此方法只有冒泡阶段。
                    elem.attachEvent(type, handler);
                }

            }
       },
       remove: function(elem, event, namespace){
            if( elem.nodeType != 1 ) return;

            var events, types, 
                elemData = dataPriv.get(elem);

            events = elemData.events || {};

            // off()  remove  all event
            if( event === undefined ){
                for( var key in events ){
                    event = key;
                    types = events[key];

                    off.call( elem, event, types, namespace );
                }
            }else{
                types = events[event];
                
                off.call( elem, event, types, namespace );
            }

            return elem;
       },
   }

    Tangth.fn.extend({
        on: function(event, fun){
            return on( this, event, fun );
        },
        off: function(event){
            var elem = this,
                eventArr, 
                namespace;

            // 以 dot / space 分割
            if( event && typeof event === "string" ){
                eventArr = event.split(/\.|\s/);
                event = eventArr[0];
                namespace = eventArr[1];
            }
            
            // console.log("off", event, namespace);

            // console.log("off", event);

            return elem.each( function() {
                this.nodeType == 1 ? Tangth.event.remove( this, event, namespace): null;
            } );
        }
    })

    Tangth.extend({
        // Unique for each copy of Tangth on the page
        // 页面上Tangth的每个副本都是惟一的
        expando: "Tangth" + ( version + Math.random() ).replace( /\D/g, "" ),
        // A global GUID counter for objects
        // 对象的全局GUID计数器
        guid: 1,
        isArray: function(obj){
            return typeof Array.isArray === "function" 
                    ? Array.isArray(obj)
                    : toString.call(obj) === "[object Array]";
                    // Object.prototype.toString.call(obj) === "[object Array]"
        },
        isPlainObject: function( obj ){
            // Detect obvious negatives
            // 检测到明显的否定
            // Use toString instead of jQuery.type to catch host objects
            // 使用toString代替jQuery。键入以捕获主机对象
            if ( !obj || toString.call( obj ) !== "[object Object]") {
                return false;
            }else{
                return true;
            }
        },
        isEmptyObject: function( obj ) {
            // 不是一个有值的对象都 返回 true
            if( !Tangth.isPlainObject(obj) ) return true;
            var name;
            for ( name in obj ) {
                return false;
            }
            return true;
        },
        isEmptyString: function( str ){
            // 不是一个有值的字符串都返回true
            if( typeof str === "string" && str.length > 0 ){
                return false;
            }
            return true;
        },
        isFunction: function( obj ) {
            return typeof obj === "function";
        },
        each: function( obj, callback ) {
            var length, i = 0;
    
            if ( Tangth.isArray( obj ) ) {
                length = obj.length;
                for ( ; i < length; i++ ) {
                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                        break;
                    }
                }
            } else {
                for ( i in obj ) {
                    if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                        break;
                    }
                }
            }
    
            return obj;
        },
        acceptData: function( owner ) {

            // Accepts only:
            //  1. Node  12 种不同的节点类型
            //    - Node.ELEMENT_NODE  -> 1
            //    - Node.DOCUMENT_NODE -> 9
            //  2. Object
            //    - Any
            return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
        },
        reg: reg,
        // 克隆事件
        cloneCopyEvent: function ( src, dest ) {
            var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events, event, eventObj;
        
            if ( dest.nodeType !== 1 ) {
                return;
            }
        
            // 1. Copy private data: events, handlers, etc.
            if ( dataPriv.hasData( src ) ) {
                pdataOld = dataPriv.access( src );
                pdataCur = dataPriv.set( dest, pdataOld );
                events = pdataOld.events;

                console.log("pdataOld", pdataOld);
        
                if ( events ) {
                    delete pdataCur.handle;
                    pdataCur.events = {};
        
                    for ( event in events ) {
                        for ( i = 0, l = events[ event ].length; i < l; i++ ) {
                            eventObj = events[event][i];
                            type = eventObj.type;
                            event.namespace ? type += "." + event.namespace: null;

                            Tangth.event.add( dest, type, eventObj.handler)
                        }
                    }
                }
            }
        
            // 2. Copy user data
            if ( dataUser.hasData( src ) ) {
                udataOld = dataUser.access( src );
                udataCur = Tangth.extend( {}, udataOld );
        
                dataUser.set( dest, udataCur );
            }
        }
    })

    // 存储数据
    function Datath(){
        this.expando = Tangth.expando + Datath.uid++;
    }

    Datath.uid = 1;

    Datath.prototype = {
        cache: function( owner ){
            // 检查owner对象是否已经有缓存
            // Check if the owner object already has a cache
            var value = owner[ this.expando ];

            // If not, create one
            if ( !value ) {
                value = {};

                // We can accept data for non-element nodes in modern browsers,
                // 我们可以接受现代浏览器中非元素节点的数据，
                // but we should not, see #8335.  但我们不应该看到8335号。
                // Always return an empty object.   总是返回一个空对象。
                if ( Tangth.acceptData( owner ) ) {

                    // If it is a node unlikely to be stringify-ed or looped over
                    // 如果它是一个不太可能被字符串化或循环的节点
                    // use plain assignment
                    // 使用普通的作业
                    if ( owner.nodeType ) {
                        owner[ this.expando ] = value;

                    // Otherwise secure it in a non-enumerable property
                    // 否则，将其保存在不可枚举的属性中
                    // configurable must be true to allow the property to be
                    // 可配置必须为true，才能允许属性为
                    // deleted when data is removed
                    // 删除数据时删除
                    } else {
                        console.log("owner", owner, this);
                        // 方法会直接在一个对象上定义一个新属性，或者修改一个对象的现有属性， 并返回这个对象
                        Object.defineProperty( owner, this.expando, {
                            value: value,
                            configurable: true
                        } );
                    }
                }
            }

            return value;
        },
        set: function( owner, data, value ) {
            var prop,
                cache = this.cache( owner );
    
            // Handle: [ owner, key, value ] args
            // Always use camelCase key (gh-2257)
            if ( typeof data === "string" ) {
                cache[ data ] = value;
    
            // Handle: [ owner, { properties } ] args
            } else {
    
                // Copy the properties one-by-one to the cache object
                for ( prop in data ) {
                    cache[ prop ] = data[ prop ];
                }
            }
            return cache;
        },
        get: function( owner, key ) {
            return key === undefined ?
                this.cache( owner ) :
    
                // Always use camelCase key (gh-2257)
                // 总是使用camelCase键
                owner[ this.expando ] && owner[ this.expando ][ key ];
        },
        access: function( owner, key, value ) {

            // In cases where either:
            //
            //   1. No key was specified
            //   2. A string key was specified, but no value provided
            //
            // Take the "read" path and allow the get method to determine
            // which value to return, respectively either:
            //
            //   1. The entire cache object
            //   2. The data stored at the key
            //
            if ( key === undefined ||
                    ( ( key && typeof key === "string" ) && value === undefined ) ) {
    
                return this.get( owner, key );
            }
    
            // When the key is not a string, or both a key and value
            // are specified, set or extend (existing objects) with either:
            //
            //   1. An object of properties
            //   2. A key and value
            //
            this.set( owner, key, value );
    
            // Since the "set" path can have two possible entry points
            // return the expected data based on which path was taken[*]
            return value !== undefined ? value : key;
        },
        remove: function( owner, key ) {
            var i = 0, 
                long,
                val,
                cache = owner[ this.expando ];
    
            if ( cache === undefined ) {
                return;
            }
    
            // 移除指定的 key
            if ( key !== undefined ) {
    
                // Support array or dot or space separated string of keys
                // 支持数组或空格分隔的键串
                if(typeof key === 'string'){
                    key = key.split(/.|\s/);
                }
                // 转化为数组
                if ( Tangth.isArray( key ) ) {
                    // 找到要删除的目标
                    long = key.length;
                    do{
                        val = cache[ key[i] ];
                        if( val ===undefind ) break;
                    }while( ++i < long )
                    
                    // 删除它
                    // 在严格模式下删除不合格标识符
                    // if( val !== undefind ) delete val;
                }
            }
    
            // Remove the expando if there's no more data
            // key === undefined  移除全部
            // Tangth.isEmptyObject( cache ) 没有更多数据
            if ( key === undefined || Tangth.isEmptyObject( cache ) ) {
                // 元素节点
                if ( owner.nodeType ) {   
                    owner[ this.expando ] = undefined;
                } else {
                    delete owner[ this.expando ];
                }
            }
        },
        hasData: function( owner ) {
            var cache = owner[ this.expando ];
            return cache !== undefined && !Tangth.isEmptyObject( cache );
        }
    };
    // 单个
    var dataPriv = new Datath();

    var dataUser = new Datath();



    if ( !noGlobal ) {
        window.Tangth = window.T = Tangth;
        window.$ === undefined && (window.$ = Tangth);  // $有无设置
    }
} )

// ajax
;(function( window, undefined ){
    "use strict";

    var $ = window.Tangth;

    // ajax
    $.extend({
        getParams: function(data) {
            if(!Tangth.isPlainObject(data)) return data;

            var arr = [];
            for (var param in data){
                arr.push(encodeURIComponent(param) + '=' +encodeURIComponent(data[param]));
            }
            return arr.join('&');
        },
        /**
         * 
            contents: {
                xml: /\bxml\b/,
                html: /\bhtml/,
                json: /\bjson\b/
            },
    
            responseFields: {
                xml: "responseXML",
                text: "responseText",
                json: "responseJSON"
            },
    
            // Data converters 数据转换器
            // Keys separate source (or catchall "*") and destination types with a single space
            // 键用一个空格分隔源(或包含所有“*”)和目标类型
            converters: {
    
                // Convert anything to text  将任何内容转换为文本
                "* text": String,
    
                // Text to html (true = no transformation) 文本到html (true =无转换)
                "text html": true,
    
                // Evaluate text as a json expression  将文本计算为json表达式
                "text json": JSON.parse,
    
                // Parse text as xml  将文本解析为xml
                "text xml": jQuery.parseXML
            }
         * 
         * */ 
        ajaxSettings: {
            url: location.href,
            type: "GET",
            async: true,  // 异步
            contentType: "application/x-www-form-urlencoded; charset=UTF-8", 
            accepts: "*"
        },
        ajaxHeader: function(xhr, obj){
            var defaluts = {
                'Content-Type': "application/x-www-form-urlencoded; charset=UTF-8",  // application/json;charset=utf-8
                "Accept-Language": "en-US", // zh-CN
                'accepts': "*",
            }
            var setting = {
                "accepts": {  // 告诉服务器什么样的响应会接受返回  // 不懂
                    "*": "*/*",  // 全部
                    text: "text/plain",
                    html: "text/html",
                    xml: "application/xml, text/xml",
                    json: "application/json, text/javascript"  
                },
            }
            if( Tangth.isPlainObject(obj) ){
                for(var key in obj){
                    var val = obj[key] || defaluts[key];
                    if( key === "accepts" ){
                        val = setting.accepts[val] || defaluts[key];
                    }

                    if(val){
                        xhr.setRequestHeader( key, val );
                    }
                }
            }
            
            /**
             * setRequestHeader() 设置请求头 
                语法： setRequestHeader(name, value)
                name 头部的名称：这个参数不应该包括空白、冒号或换行
                value 头部的值：这个参数不应该包括换行
                约束：此方法设置请求头信息，必须在 调用 open( ) 之后 且 调用 send( ) 之前
            */    

        },
        ajax: function( url, opts){
            // If url is an object
            if ( typeof url === "object" ) {
                opts = url;
                url = undefined;
            }

            var options, params, type, data, headers, xhr;

            options = $.extend(true, Tangth.ajaxSettings, opts);

           // 传入方式默认为对象
           // 默认为GET请求
           url = options.url;
           type = (options.type || "GET").toUpperCase();
           data = options.data;

           // 返回值类型默认为json
           if( Tangth.isPlainObject(data) ){
                if(type === 'POST'){
                    data = JSON.stringify(data);
                }else if(type === 'GET'){
                    // 对需要传入的参数的处理
                    url += '?' + Tangth.getParams(data) || ""; 
                }
           }

           if( Tangth.isPlainObject(options.headers) ){
                headers = options.headers;
           }else{
                headers = {};
           }
           headers["Content-Type"] = options.contentType;
           headers["accepts"] = options.accepts;
       
           /**
            * 创建一个 ajax请求
            * W3C标准和IE标准
            */
           if (window.XMLHttpRequest){
               // W3C标准
               xhr = new XMLHttpRequest();
           }else{
               /**
                * IE标准
                * @type {ActiveXObject}
                */
               xhr = new ActiveXObject('Microsoft.XMLHTTP')
           }
           xhr.onreadystatechange = function () {
               if (xhr.readyState == 4){
                   var status = xhr.status;
                   if (status >= 200 && status < 300 || status === 304){
                       // console.log(xhr.responseText, xhr);
                       var res = xhr.responseText;
                       try{
                           res = JSON.parse(res);
                       }catch(e){
   
                       }finally{
                           options.success && options.success( res, xhr.responseXML);
                       }
                   }else{
                       options.fail && options.fail(status);
                   }
               }
           };
           if (type == 'GET'){
               xhr.open("GET", url, options.async);
               xhr.send(null)
           }else if (type == 'POST'){
               // 打开请求
               xhr.open('POST', url, options.async);
               // POST请求设置请求头
               Tangth.ajaxHeader(xhr, headers);
               // 发送请求参数
               xhr.send(data);
           }
        }
    })

})(typeof window !== "undefined" ? window : this)


// 属性 (属性、css类名、html代码/文本/值)
;(function( window, undefined ){
    "use strict";

    var $ = window.Tangth;

    /**
     *  1.innerText是标签内的文本,输入输出的是字符串;
        2.innerHtml是<标签内的文本,输入输出到该DOM内部纯HTML代码;
        3.value是表单元素特有的属性,输入输出的是字符串;
     * */ 

    function setVal(value, attr){
        var elem;
        var long = this.length;

        for(var i=0; i<long; i++){
            elem = this[i];

            // 如果节点是元素节点，则 nodeType 属性将返回 1。
            // 如果节点是属性节点，则 nodeType 属性将返回 2。
            if(elem.nodeType == 1){ 
                // 获取值
                if(value === undefined){
                    return elem[attr];
                }else if( elem[attr] !== undefined ){
                    // 设置值
                    elem[attr] = value;
                }
            }
        }

        return this;
    }

    var rnotwhite = ( /\S+/g );
    function getClass( elem ) {
        return elem.getAttribute && elem.getAttribute( "class" ) || "";
    }

    $.fn.extend({
        attr: function(name, value){
            // "" / null / undefined  直接返回
            if(!name) return;

            var elem, result;
            var long = this.length;

            for(var i=0; i<long; i++){
                elem = this[i];

                // 如果节点是元素节点，则 nodeType 属性将返回 1。
                // 如果节点是属性节点，则 nodeType 属性将返回 2。
                if(elem.nodeType == 1){ 
                    if(typeof name === "string"){
                        // 设置属性
                        if(value !== undefined){
                            elem.setAttribute(name, value);
                        // 获取属性
                        }else{
                            // 只返回第一个的属性值
                            result = elem.getAttribute(name);
                            return result;
                        }
                    }else if(Tangth.isPlainObject(name)){
                        for(var key in name){
                            elem.setAttribute(key, name[key] || "");                            
                        }
                    }
                }else{
                    return this;
                }
            }

            return this;
        },
        // 此函数返回一个HTML字符串
        html: function(value){
            return setVal.call(this, value, "innerHTML");
        },
        // 此函数返回一个要设置的值
        val: function(value){
            return setVal.call(this, value, "value");
        },
        // 此函数返回一个字符串
        text: function( value ) {
            return setVal.call(this, value, "innerText");            
        },

        addClass: function( value ){
            var curValue, finalValue, elem, valArr, classArr,
                i = 0;

            // 判断是否包含该类
            if( typeof value === "string" && value ){
                valArr = value.match( rnotwhite ) || [];
            }
            
            this.each(function(){
                elem = this;
                if( elem.nodeType == 1 ){
                    // 获取class类名
                    curValue = getClass( elem );
                    finalValue = curValue || "";

                    classArr = curValue.match( rnotwhite ) || [];

                    $.each( valArr, function(i, val){
                        // 没有该类
                        if( !classArr.includes(val) ){
                            finalValue ? finalValue += " ": null;
                            finalValue += val;
                        }
                    })

                    if ( curValue !== finalValue ) {
						elem.setAttribute( "class", finalValue );
					}
                }
            })
            
            return this;

        },
        removeClass: function( value ){
            var curValue, finalValue, elem, valArr, classArr,
            i = 0;

            // 判断是否包含该类
            if( typeof value === "string" && value ){
                valArr = value.match( rnotwhite ) || [];
            }
            
            this.each(function(){
                elem = this;
                if( elem.nodeType == 1 ){
                    // 获取class类名
                    curValue = getClass( elem );
                    finalValue = "";

                    classArr = curValue.match( rnotwhite ) || [];

                    $.each( classArr, function(i, val){
                        // 不需要移除该类
                        if( !valArr.includes(val) ){
                            finalValue ? finalValue += " ": null;
                            finalValue += val;
                        }
                    })

                    if ( curValue !== finalValue ) {
                        elem.setAttribute( "class", finalValue );
                    }
                }
            })
            
            return this;

        }
    })

})( typeof window !== "undefined" ? window : this)


// css (css、 位置、尺寸)
;(function( window, undefined ){
    "use strict";

    var $ = window.Tangth;

    $.fn.extend({
        /**
         * 1. 直接设置style的属性  某些情况用这个设置 !important值无效
            如果属性有'-'号，就写成驼峰的形式（如textAlign）  如果想保留 - 号，就中括号的形式  element.style['text-align'] = '100px';
            element.style.height = '100px';

            2. 直接设置属性（只能用于某些属性，相关样式会自动识别）
            element.setAttribute('height', 100);
            element.setAttribute('height', '100px');

            3. 设置style的属性
            element.setAttribute('style', 'height: 100px !important');

            4. 使用setProperty  如果要设置!important，推荐用这种方法设置第三个参数
            element.style.setProperty('height', '300px', 'important');

            5. 改变class   比如JQ的更改class相关方法
            因JS获取不到css的伪元素，所以可以通过改变伪元素父级的class来动态更改伪元素的样式
            element.className = 'blue';
            element.className += 'blue fb';

            6. 设置cssText
            element.style.cssText = 'height: 100px !important';
            element.style.cssText += 'height: 100px !important';
         * 
         * */ 
        css: function(property, value){
            if(!property) return;
            var elem, long = this.length, result = "";

            for(var i=0; i<long; i++){
                elem = this[i];

                // console.log("elem", elem);
                // 如果节点是元素节点，则 nodeType 属性将返回 1。
                // 如果节点是属性节点，则 nodeType 属性将返回 2。
                if(elem.nodeType == 1){ 
                    if(typeof property === "string"){
                        // 设置属性
                        if(arguments.length >= 2){
                            elem.style[property] = value;
                        // 获取属性
                        }else{
                            // 只返回第一个的属性值
                            result = elem.style[property];
                            return result;
                        }
                    }else if(Tangth.isPlainObject(property)){
                        for( key in property){
                            elem.style[property] = property[key];
                        }
                    }
                }else{
                    return this;
                }
            }
            return this;
        },
    })

})( typeof window !== "undefined" ? window : this)


// 文档处理 (内部插入、外部插入、替换、删除、复制)
;(function( window, undefind ){
    "use strict";

    var $ = window.Tangth;

    var acceptData = function( owner ) {

        // Accepts only:
        //  1. Node  12 种不同的节点类型
        //    - Node.ELEMENT_NODE  -> 1
        //    - Node.DOCUMENT_NODE -> 9      代表整个文档（DOM 树的根节点）
        //    - Node.DocumentFragment -> 11  代表轻量级的 Document 对象，能够容纳文档的某个部分
        //   	Text                 -> 3     代表元素或属性中的文本内容。
        //  	Attr                 -> 2	  代表属性

        return owner.nodeType === 1 || owner.nodeType === 9 || owner.nodeType === 11 || owner.nodeType === 3;
    }

    var eachElem = function(callback){
        var long = this.length,
            i = 0,
            elem;

        for( ; i<long; i++){
            elem = this[i];
            callback(elem)
        }
    }

    // Dom manipulation
    // Dom 操作
    var domManip = function( collection, args, callback ){
        var match, matchLast, matchMiddle, newNode, long = collection.length;
        
        args = [].concat.apply( [], args );

        eachElem.call( collection, function(elem){
            $.each(args, function(i, val){
                // createTextNode
                // createElement
                if( typeof val === "string" ){
                    match = $.reg.elem.exec(val);
                    matchLast = $.reg.elemLast.exec(val);
                    // append('<span>');    append("<b>Hello</b>");
                    if( match && match.length > 0){
                        match = match[0];
                        newNode = document.createElement(match.replace( $.reg.filterTag, ""));

                        if( matchLast && matchLast.length > 0){
                            matchMiddle = val.replace( $.reg.elem, "").replace( $.reg.elemLast, "");
                            
                            newNode = long > 1? $.clone( $(match).append(matchMiddle)[0], true ) : $(match).append(matchMiddle)[0];
                        }
                    }else{
                        // .append('厉害啦!')
                        newNode = document.createTextNode(val);  
                    }

                }else{
                    // append($('<span>'))
                    newNode = long > 1?  $.clone(val[0], true) : val[0]; 
                }

                callback.call(elem, newNode);
            })
        });

        return collection;
    }

    $.extend({
        contains: function( context, elem ) {
            // Set document vars if needed
            if ( ( context.ownerDocument || context ) !== document ) {
                setDocument( context );
            }
            return contains( context, elem );
        },
        clone: function( elem, dataAndEvents ) {
            var clone = elem.cloneNode( true ); // true  为克隆所有后代
    
            // 克隆数据和事件
            if( dataAndEvents ){
                $.cloneCopyEvent( elem, clone );
            }

            // Return the cloned set
            return clone;
        },
    })

    $.fn.extend({
        append: function(){
            return domManip( this, arguments, function( elem ) {
                if ( acceptData(elem) ) {
                    // var target = manipulationTarget( this, elem );
                    // console.log({"elem": elem});
                    this.appendChild( elem );
                }
            } );
        }
    })


})(typeof window !== "undefined" ? window : this)


// 选择器 (基本、层级、内容、属性、表单、表单对象属性)
;(function( window, undefind ){
    "use strict";
    var $ = window.Tangth;

})(typeof window !== "undefined" ? window : this)


// 筛选 (过滤、查找、串联)
;(function( window, undefind ){
    "use strict";

    var $ = window.Tangth;


    $.fn.extend({
        eq: function( i ) {
            var len = this.length,
                j = +i + ( i < 0 ? len : 0 );
            return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
        },
        
    })

})(typeof window !== "undefined" ? window : this)


// 事件
;(function( window, undefind ){
    "use strict";

    var $ = window.Tangth;

    function trigger(li){
        // IE
        if(document.all) {
            li.click();
        }
        // 其它浏览器
        else {
            var e = document.createEvent("MouseEvents");
            e.initEvent("click", true, true);
            li.dispatchEvent(e);
        }
    }

    $.fn.extend({
        trigger: function(){
            var elem, long = this.length, result = "";

            for(var i=0; i<long; i++){
                elem = this[i];

                if( elem.nodeType == 1 ){
                    trigger( elem );
                }
            }

            return this;
        }
    })


})(typeof window !== "undefined" ? window : this)


// 效果 (基本、滑动、淡入淡出、自定义)
;(function( window, undefind ){
    "use strict";

    var $ = window.Tangth;


})(typeof window !== "undefined" ? window : this)



